﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Windows;
//using System.Windows.Controls.DataVisualization.Charting;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;
using System.Windows.Media.Animation;
using RytmSerca.Ekg;
using Visiblox.Charts;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace RytmSerca
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            BindingErrorTraceListener.SetTrace();
            DataContext = this;
            InitializeComponent();
        }

        #region Propreties
        private static readonly DependencyProperty showResultProperty =
            DependencyProperty.Register("ShowResult", typeof(bool), typeof(MainWindow), new PropertyMetadata(false));
        private static readonly DependencyProperty chartLoadedProperty =
            DependencyProperty.Register("ChartLoaded", typeof(bool), typeof(MainWindow), new PropertyMetadata(false));
        private static readonly DependencyProperty chartLoadingProperty =
            DependencyProperty.Register("ChartLoading", typeof(bool), typeof(MainWindow), new PropertyMetadata(false));
        private static readonly DependencyProperty showInstructionsProperty =
            DependencyProperty.Register("ShowInstructions", typeof(bool), typeof(MainWindow), new PropertyMetadata(true));
        private static readonly DependencyProperty sourceFileNameProperty =
            DependencyProperty.Register("SourceFileName", typeof(string), typeof(MainWindow), new PropertyMetadata(""));
        private static readonly DependencyProperty rPeaksProperty =
            DependencyProperty.Register("RPeaks", typeof(List<EkgPoint>), typeof(MainWindow), new PropertyMetadata(null));
        private static readonly DependencyProperty signalProperty =
            DependencyProperty.Register("Signal", typeof(DataSeries<double, double>), typeof(MainWindow), new PropertyMetadata(null));
        private static readonly DependencyProperty heartBeatProperty =
            DependencyProperty.Register("HeartBeat", typeof(double), typeof(MainWindow), new PropertyMetadata(null));
        private static readonly DependencyProperty inProgressProperty =
            DependencyProperty.Register("InProgress", typeof(bool), typeof(MainWindow), new PropertyMetadata(false));
        public static readonly DependencyProperty ParametersProperty =
            DependencyProperty.Register("Parameters", typeof(AnalyseParameters), typeof(MainWindow), new UIPropertyMetadata(new AnalyseParameters()));
        public static readonly DependencyProperty SampleLimitLog2Property =
            DependencyProperty.Register("SampleLimitLog2", typeof(int), typeof(MainWindow), new UIPropertyMetadata(14));



        private EkgSignal _signal;
        private LineSeries _rPeaksSeries;
        private AnalyseParameters _parameters;


        public int SampleLimitLog2
        {
            get { return (int)GetValue(SampleLimitLog2Property); }
            set { SetValue(SampleLimitLog2Property, value); }
        }
        public AnalyseParameters Parameters
        {
            get { return (AnalyseParameters)GetValue(ParametersProperty); }
            set { SetValue(ParametersProperty, value); }
        }
        
        /// <summary>
        /// Pokazuj "wyliczony rytm serca" itd.
        /// </summary>
        bool ShowResult
        {
            get { return (bool)GetValue(showResultProperty); }
            set { SetValue(showResultProperty, value); }
        }
        /// <summary>
        /// Czy wykres widoczny
        /// </summary>
        bool ChartLoaded
        {
            get { return (bool)GetValue(chartLoadedProperty); }
            set { SetValue(chartLoadedProperty, value); }
        }
        bool ChartLoading
        {
            get { return (bool)GetValue(chartLoadingProperty); }
            set { SetValue(chartLoadingProperty, value); }
        }
        /// <summary>
        /// Czy instrukcje pośrodku co robić widoczne
        /// </summary>
        bool ShowInstructions
        {
            get { return (bool)GetValue(showInstructionsProperty); }
            set { SetValue(showInstructionsProperty, value); }
        }
        /// <summary>
        /// Plik źródłowy z danymi EKG
        /// </summary>
        string SourceFileName
        {
            get { return (string)GetValue(sourceFileNameProperty); }
            set { SetValue(sourceFileNameProperty, value); }
        }
        /// <summary>
        /// Tak jeśli w trakcie roboty, czyli czy wyświetlać pasek postępu na dole.
        /// </summary>
        bool InProgress
        {
            get { return (bool)GetValue(inProgressProperty); }
            set
            {
                SetValue(inProgressProperty, value);
            }
        }
        /// <summary>
        /// Struktura przechowująca wynik działania algorytmu.
        /// </summary>
        List<EkgPoint> RPeaks
        {
            get { return (List<EkgPoint>)GetValue(rPeaksProperty); }
            set { SetValue(rPeaksProperty, value); }
        }
        /// <summary>
        /// Wczytane dane w postaci pierwotnej.
        /// </summary>
        DataSeries<double, double> Signal
        {
            get { return (DataSeries<double, double>)GetValue(signalProperty); }
            set { SetValue(signalProperty, value); }
        }
        /// <summary>
        /// Lista "załamków R", które mają być oznaczone na wykresie.
        /// </summary>
        double HeartBeat
        {
            get { return (double)GetValue(heartBeatProperty); }
            set { SetValue(heartBeatProperty, value); }
        }

        #endregion

        private void ChooseFileButton_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new OpenFileDialog
                         {
                             CheckFileExists = true,
                             Multiselect = false
                         };
            if (dialog.ShowDialog() == true)
            {
                string sourceFn = SourceFileName = dialog.FileName;

                //TODO: wrzucić do try-catch
                ChartLoading = true;
                ShowInstructions = false;
                ChartLoaded = false;
                theChart.Series.Clear();

                var reader = new EkgSignalReader();
                reader.SampleLimit = 1 << SampleLimitLog2;
                bool error = false;
                try
                {
                    _signal = reader.ReadFile(sourceFn);
                }
                catch (Exception ex) {
                    MessageBox.Show(ex.Message, "Błąd przy wczytywaniu pliku", MessageBoxButton.OK, MessageBoxImage.Error);
                                        ChartLoading = false;
                    error = true;
                }

                if (!error)
                {
                    RPeaks = null;
                    HeartBeat = 0;
                    ShowInstructions = false;
                    ShowResult = false;
                    ChartLoaded = true;
                    ChartLoading = false;
                    SourceFileName = sourceFn;

                    var ekgSeries = SetSignal();

                    theChart.Series.Add(ekgSeries);
                }
            }
        }

        private LineSeries SetSignal()
        {
            var ekgSeries = new LineSeries();
            Signal = new DataSeries<double, double>("Sygnał EKG", _signal.Select(ep => new DataPoint<double, double>(ep.Time, ep.Value)));
            ekgSeries.LineStroke = Brushes.Red;
            ekgSeries.DataSeries = Signal;
            SetRanges();
            return ekgSeries;
        }

        private void SetRanges()
        {
            xAxis.Range = new DoubleRange(0, _signal.EndTime);
            yAxis.Range = new DoubleRange(_signal.Min, _signal.Max);
        }

        private void AnalyseButton_Click(object sender, RoutedEventArgs e)
        {
            if (Signal == null || Signal.Count == 0)
                MessageBox.Show("Najpierw trzeba wczytać dane wejściowe", "Brak danych", MessageBoxButton.OK,
                                MessageBoxImage.Exclamation);
            else
                Analyse();
        }


        private void Analyse()
        {
            // testowy, losowy sygnał

            InProgress = true;
            ShowResult = false;
            //Signal = null;
            RPeaks = null;
            _signal.Parameters = Parameters;

            Action action = _signal.Analyse;
            action.BeginInvoke(AnalyseFinished, null);
        }


        private void AnalyseFinished(IAsyncResult ar)
        {
            if (ar.IsCompleted)
            {
                Dispatcher.Invoke(new Action(delegate
                    {
                        RPeaks = _signal.RPeaks;
                        HeartBeat = _signal.BeatsPerMinute;
                        if (Parameters.ReturnFilteredSignal)
                        {
                            theChart.Series.Clear();
                            //Signal = null;
                            var ekgSerie = SetSignal();
                            theChart.Series.Add(ekgSerie);
                        }
                        if (theChart.Series.Contains(_rPeaksSeries))
                            theChart.Series.Remove(_rPeaksSeries);


                        _rPeaksSeries = new LineSeries();
                        var data = new DataSeries<double, double>("Załamki R", _signal.RPeaks.Select(ep => new DataPoint<double, double>(ep.Time, ep.Value)));
                        _rPeaksSeries.ShowLine = false;
                        _rPeaksSeries.ShowPoints = true;
                        _rPeaksSeries.PointFill = Brushes.Black;
                        _rPeaksSeries.DataSeries = data;

                        theChart.Series.Add(_rPeaksSeries);

                        InProgress = false;
                        ShowResult = true;
                    }));

            }
        }

        private void ParametersButton_Click(object sender, RoutedEventArgs e)
        {

        }

        private void ReturnedFilteredLabel_MouseUp(object sender, MouseButtonEventArgs e)
        {
            ReturnedFilteredCheck.IsChecked = !ReturnedFilteredCheck.IsChecked;
        }

        //private void FFTButton_Click(object sender, RoutedEventArgs e)
        //{

        //}

    }
    public class AlternativeConverter : IMultiValueConverter
    {
        private static IMultiValueConverter _instance = new AlternativeConverter();

        public static IMultiValueConverter Instance
        {
            get { return _instance; }
        }

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return values.Select(value => value is bool ? (bool?)(bool)value : null)
                .Where(option => option != null)
                .Aggregate(false, (current, option) => current || (option ?? false));
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    public class ConjunctionConverter : IMultiValueConverter
    {
        private static IMultiValueConverter _instance = new ConjunctionConverter();

        public static IMultiValueConverter Instance
        {
            get { return _instance; }
        }

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return values.Select(value => value is bool ? (bool?)(bool)value : null)
                .Where(option => option != null)
                .Aggregate(true, (current, option) => current && (option ?? true));
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
}